package edu.up.firesawakening;

import android.graphics.Color;
import android.os.Bundle;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.TextView;
import edu.up.firesawakening.TacticsGame.Move;
import edu.up.game.GameHumanPlayer;

/**
 * A player that is controlled by a human. Displays the gamestate and responds
 * to input.
 * 
 * @author Alex Hanemann
 * @author Matt Delaney
 * @author Viet Phan
 * @version 19 November 2012
 */
public class TacticsHumanPlayer extends GameHumanPlayer implements
		OnTouchListener, OnClickListener {

	/*
	 * ====================================================================
	 * Static Variables
	 * --------------------------------------------------------------------
	 */
	public static final String UPPERX = "uppery";
	public static final String UPPERY = "upperx";
	public static final String MOVES = "moves";
	public static final String SELECT = "select";
	public static final String TARGET = "target";
	public static final String LASTX = "lastx";
	public static final String LASTY = "lasty";
	public static final String TOUCHX = "touchx";
	public static final String TOUCHY = "touchy";

	/**
	 * The currently selected source tile. Used for GUI, and determining which
	 * unit the player wants to move
	 */
	protected Tile m_selectedTile;

	/**
	 * The currently selected target tile. Used for GUI, and determining which
	 * unit the player wants to move
	 */
	protected Tile m_targetTile;

	/**
	 * This array indicates which tiles on the grid should be highlighted, and
	 * how
	 */
	protected Move[][] m_possibleMoves = null;

	/**
	 * The game the player can use
	 */
	protected TacticsGame m_tg;

	/**
	 * The surface view to display the board on
	 */
	protected TacticsSurfaceView m_tsv;

	/**
	 * The textbox used for output
	 */
	protected TextView m_textBox;

	/**
	 * Button to attack
	 */
	protected Button m_attackButton;

	/**
	 * Button to move
	 */
	protected Button m_moveButton;

	/**
	 * Button to end turn
	 */
	protected Button m_endTurnButton;

	/**
	 * Player id number
	 */
	protected int m_id;

	/**
	 * Upper left x coord of display
	 */
	protected int m_dispUpperLeftX;

	/**
	 * Upper left y coord of display
	 */
	protected int m_dispUpperLeftY;

	protected int m_lastDispUpperLeftX;
	protected int m_lastDispUpperLeftY;
	protected int m_touchStartX;
	protected int m_touchStartY;

	/**
	 * the New Features button
	 */
	protected Button m_newFeaturesButton;
	protected Button m_creditsButton;

	/**
	 * Initializes the player and the GUI. Performs any actions that would be
	 * expected from onCreate
	 * 
	 * @param savedInstanceState
	 */
	@Override
	public void initializeGUI(Bundle savedInstanceState) {

		// load the XML
		setContentView(edu.up.game.R.layout.tactics_human_player);

		// get the game
		m_tg = (TacticsGame) game;

		m_id = m_tg.whoseTurn();

		// get the views we'll need to manipulate
		m_moveButton = (Button) findViewById(edu.up.game.R.id.moveButton);
		m_attackButton = (Button) findViewById(edu.up.game.R.id.attackButton);
		m_endTurnButton = (Button) findViewById(edu.up.game.R.id.endTurnButton);
		m_textBox = (TextView) findViewById(edu.up.game.R.id.tacticsInfoBox);
		m_tsv = (TacticsSurfaceView) findViewById(edu.up.game.R.id.tacticsBoard);
		m_newFeaturesButton = (Button) findViewById(edu.up.game.R.id.newFeaturesButton);
		m_creditsButton = (Button) findViewById(edu.up.game.R.id.creditsButton);

		m_textBox.setText("I am player " + m_id);

		m_endTurnButton.setClickable(true); // can always end your turn
		m_endTurnButton.setBackgroundColor(Color.RED);

		m_moveButton.setOnClickListener(this);
		m_attackButton.setOnClickListener(this);
		m_endTurnButton.setOnClickListener(this);

		// the NF button is only on one mode, so we need to ensure it's not
		// null!
		if (m_newFeaturesButton != null) {
			m_newFeaturesButton.setOnClickListener(this);
		}
		if (m_creditsButton != null) {
			m_creditsButton.setOnClickListener(this);
		}

		// set buttons to not be selectable (will be selectable when a tile is
		// selected)
		enableMoveButton(false);
		enableAttackButton(false);

		m_dispUpperLeftX = m_dispUpperLeftY = m_lastDispUpperLeftX = m_lastDispUpperLeftY = 0;
		m_touchStartX = m_touchStartY = -1;

		if (savedInstanceState != null) {
			// pretty sure we don't need all these things
			m_dispUpperLeftX = savedInstanceState.getInt(UPPERX);
			m_dispUpperLeftY = savedInstanceState.getInt(UPPERY);
			m_lastDispUpperLeftX = savedInstanceState.getInt(LASTX);
			m_lastDispUpperLeftY = savedInstanceState.getInt(LASTY);
			m_touchStartX = savedInstanceState.getInt(TOUCHX);
			m_touchStartY = savedInstanceState.getInt(TOUCHY);

			m_possibleMoves = (Move[][]) savedInstanceState
					.getSerializable(MOVES);
			m_selectedTile = (Tile) savedInstanceState.getSerializable(SELECT);
			m_targetTile = (Tile) savedInstanceState.getSerializable(TARGET);
		}

		// set up the surface view
		m_tsv.setWillNotDraw(false);
		m_tsv.setOnTouchListener(this);
		m_tsv.updateTSV(m_tg.getMap(), m_possibleMoves, m_selectedTile,
				m_targetTile, m_dispUpperLeftX, m_dispUpperLeftY);

	}

	@Override
	public void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		outState.putSerializable(MOVES, m_possibleMoves);
		outState.putInt(UPPERX, m_dispUpperLeftX);
		outState.putInt(UPPERY, m_dispUpperLeftY);
		outState.putInt(LASTX, m_lastDispUpperLeftX);
		outState.putInt(LASTY, m_lastDispUpperLeftY);
		outState.putInt(TOUCHX, m_touchStartX);
		outState.putInt(TOUCHY, m_touchStartY);
		outState.putInt(UPPERX, m_dispUpperLeftX);
		outState.putInt(UPPERY, m_dispUpperLeftY);
		outState.putSerializable(SELECT, m_selectedTile);
		outState.putSerializable(TARGET, m_targetTile);
	}

	/**
	 * Updates the display according to the current state of the game
	 */
	@Override
	public void updateDisplay() {
		m_tg = (TacticsGame) game;
		m_tsv.updateTSV(m_tg.getMap(), m_possibleMoves, m_selectedTile,
				m_targetTile, m_dispUpperLeftX, m_dispUpperLeftY);
	}

	/**
	 * Enables or disables the attack button and changes the color appropriately
	 * 
	 * @param enable
	 *            true to enable, false to disable
	 */
	public void enableAttackButton(boolean enable) {
		m_attackButton.setClickable(enable);
		m_attackButton.setBackgroundColor(enable ? Color.GREEN : Color.LTGRAY);
	}

	/**
	 * Enables or disables the move button and changes the color appropriately
	 * 
	 * @param enable
	 *            true to enable, false to disable
	 */
	public void enableMoveButton(boolean enable) {
		m_moveButton.setClickable(enable);
		m_moveButton.setBackgroundColor(enable ? Color.GREEN : Color.LTGRAY);
	}

	/**
	 * Handles a touch event
	 * 
	 * @param view
	 *            the view that was touched
	 * @param event
	 *            the MotionEvent of the touch
	 */
	public boolean onTouch(View view, MotionEvent event) {

		if (view == m_tsv) {

			int xCoord = ((int) FloatMath.floor(event.getX()))
					/ TacticsSurfaceView.TILE_SIZE;
			int yCoord = ((int) FloatMath.floor(event.getY()))
					/ TacticsSurfaceView.TILE_SIZE;

			if (event.getAction() == MotionEvent.ACTION_DOWN) {
				// The user is clicking on a tile, try to select it
				m_lastDispUpperLeftX = m_dispUpperLeftX;
				m_lastDispUpperLeftY = m_dispUpperLeftY;

				m_touchStartX = xCoord;
				m_touchStartY = yCoord;

				xCoord += m_dispUpperLeftX;
				yCoord += m_dispUpperLeftY;

				handleTouchOnSurface(xCoord, yCoord);
				updateDisplay();

				return true;
			} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
				// The user is dragging to scroll
				m_dispUpperLeftX = m_lastDispUpperLeftX
						- (xCoord - m_touchStartX);
				m_dispUpperLeftY = m_lastDispUpperLeftY
						- (yCoord - m_touchStartY);

				if (m_dispUpperLeftX < 0)
					m_dispUpperLeftX = 0;

				if (m_dispUpperLeftY < 0)
					m_dispUpperLeftY = 0;

				if (m_dispUpperLeftX > m_tg.getMap().length
						- m_tsv.getDispXTiles())
					m_dispUpperLeftX = m_tg.getMap().length
							- m_tsv.getDispXTiles();

				if (m_dispUpperLeftY > m_tg.getMap()[0].length
						- m_tsv.getDispYTiles())
					m_dispUpperLeftY = m_tg.getMap()[0].length
							- m_tsv.getDispYTiles();

				if (m_dispUpperLeftX != m_lastDispUpperLeftX
						|| m_dispUpperLeftY != m_lastDispUpperLeftY) {

					updateDisplay();
				}
				return true;
			}
		}
		return false;
	}

	/**
	 * Handles all touch events from human player on the TacticsSurfaceView.
	 * 
	 * @param xCoord
	 * @param yCoord
	 */
	protected void handleTouchOnSurface(int xCoord, int yCoord) {

		// Turn off the buttons until we have a reason to turn them on again
		enableAttackButton(false);
		enableMoveButton(false);

		// Check if the selection was in bounds
		if (xCoord >= m_tg.getMap().length || yCoord >= m_tg.getMap()[0].length) {
			clearSelection();
			return;
		}

		// Get the tile that was touched
		Tile tile = m_tg.getMap()[xCoord][yCoord];

		// Check if we can select a unit here
		if (m_selectedTile == null) {
			clearSelection();

			if (tile.isOccupied() && tile.getUnit().getOwner() == m_id) {
				// If we own the unit, select it
				m_selectedTile = tile;
				m_possibleMoves = m_tg.availableActions(m_selectedTile);

				displayStats(m_selectedTile);
			} else {
				// If it is unoccupied or an enemy, just show info
				m_targetTile = tile;
				displayStats(m_targetTile);
			}

		} else {
			switch (m_possibleMoves[tile.getX()][tile.getY()].move) {
			case SELF:
				// Deselect if selected was touched
				clearSelection();
				m_textBox.setText("Deselected the current tile.");
				break;
			case MOVE:
				// Target for move
				m_targetTile = tile;
				enableMoveButton(true);
				displayStats(m_targetTile);
				break;
			case ATTACK:
				// Target for attack
				m_targetTile = tile;
				enableAttackButton(true);
				displayStats(m_targetTile);
				break;
			default:
				// No valid move here, so check for something else
				clearSelection();
				if (tile.isOccupied() && tile.getUnit().getOwner() == m_id) {
					// If we own the unit, select it
					m_selectedTile = tile;
					m_possibleMoves = m_tg.availableActions(m_selectedTile);

					displayStats(m_selectedTile);
				} else {
					// If it is unoccupied or an enemy, just show info
					m_targetTile = tile;
					displayStats(m_targetTile);
				}
				break;
			}
		}
	}

	/**
	 * Clears all selection related data
	 */
	protected void clearSelection() {
		m_possibleMoves = null;
		m_selectedTile = null;
		m_targetTile = null;
	}

	/**
	 * Displays stats for a given tile
	 * 
	 * @param t
	 *            the tile to display
	 */
	protected void displayStats(Tile t) {
		String text = "Selected tile at (" + (t.getX() + 1) + ","
				+ (t.getY() + 1) + "). Type: " + t.m_type + "\n";

		if (!t.isOccupied()) {
			m_textBox.setText(text);
			return;
		}

		Unit u = t.getUnit();
		m_textBox.setText(text + "Unit stats:\n" + "Health: " + u.getHealth()
				+ "\nAttack Power: " + u.getAtkPow() + "\nArmor: "
				+ u.getArmor());
	}

	/**
	 * Called when a view has been clicked
	 * 
	 * @param v
	 *            the view that was clicked
	 */
	public void onClick(View v) {
		if (v == m_moveButton) {
			m_textBox.setText("Move!");
			takeAction(new TacticsMoveAction(
					m_id,
					m_selectedTile,
					m_targetTile,
					m_possibleMoves[m_targetTile.getX()][m_targetTile.getY()].cost));
		} else if (v == m_attackButton) {
			m_textBox.setText("Attack!");
			takeAction(new TacticsAttackAction(
					m_id,
					m_selectedTile,
					m_targetTile,
					m_possibleMoves[m_targetTile.getX()][m_targetTile.getY()].cost));
		} else if (v == m_endTurnButton) {
			takeAction(new TacticsEndTurnAction(m_id));
		} else if (v == m_newFeaturesButton) {
			showNewFeatures();
		} else if (v == m_creditsButton) {
			showCredits();
		}

	}

	/**
	 * Displays the image credits.
	 */
	private void showCredits() {
		// Provides credits for the owner of the images.
		super.messageBox("\t \t \t \t \t \t \t \t \t CREDITS: \n \n "
				+ "Tiles: Keppli (dundjinni.com/forums/forum_posts.asp?TID=3496&KW=tile) \n \n"
				+ "Units: Michal Madej's 32 pixel dwarves (artgoblin.pl) \n \n"
				+ "Castles: Based on Jeff Scarterfield's 'How to draw a castle' (DrawCartoonsOnline.com) \n \n"
				+ "Thanks for being an awesome class everybody! We had a lot of fun with this project!");
	}

	/**
	 * Displays a string with the new features (that aren't so obvious).
	 */
	public void showNewFeatures() {
		// tell the user they can move the map
		super.messageBox("You can now drag the map around to see more of the map!");
	}

}
